﻿#region License statement
// NJamb, a specification and delta-specification DSL
// Copyright (c) 2010-2011, Mark Knell
// Published under the MIT License; all other rights reserved
#endregion

#region using...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NJamb.Specifications;
using Stile.Patterns.SelfDescribingPredicates.Evaluators;
using Stile.Readability;
using Stile.Types.Expressions;
#endregion

namespace NJamb.Parser.SpecificationBuilders.SubjectHas.Simple.Enumerable.Quantifiers
{
    public class HasAll<TSubject, TItem, TSpecifies> : EnumerableQuantifiedHas<TSubject, TItem, TSpecifies>
        where TSubject : class, IEnumerable<TItem>
        where TSpecifies : class, ISpecification
    {
        public HasAll(ICollaboratingSpecificationBuilder<TSubject, TSpecifies> specificationBuilder)
            : base(specificationBuilder) {}

        public override TSpecifies ItemsSatisfying(Expression<Func<TItem, bool>> predicateExpression)
        {
            Func<TItem, bool> compiled = predicateExpression.Compile();
            var evaluator = new Evaluator<TSubject, bool>(new NoOpExpectedClause<bool>(),
                new NoOpActualClause<bool>(),
                x => x.All(compiled),
                x => x,
                MakeCallback(predicateExpression, compiled));
            TSpecifies specifies = SpecificationBuilder.Specify(evaluator);
            return specifies;
        }

        protected override int Count
        {
            get { throw new NotImplementedException("Deliberately not implemented."); }
        }
        protected override Func<int, bool> CountPredicate
        {
            get { throw new NotImplementedException("Deliberately not implemented."); }
        }
        protected override string Qualifier
        {
            get { throw new NotImplementedException("Deliberately not implemented."); }
        }

        private static Func<TSubject, bool, string> MakeCallback(Expression<Func<TItem, bool>> predicateExpression,
            Func<TItem, bool> predicate)
        {
            return (subject, b) =>
                   {
                       int countExpected = subject.Count();
                       List<TItem> failures = subject.Where(x => !predicate.Invoke(x)).ToList();
                       int countActual = countExpected - failures.Count;
                       string failuresExplained = failures.ToDebugString();
                       string expressionExplained = ExpressionExtensions.ToDebugString(predicateExpression);
                       string expectedItems = countExpected.Pluralize(countExpected + " item", "all " + countExpected + " items");
                       string expected = string.Format("have {0} satisfying {1}", expectedItems, expressionExplained);
                       string actual = string.Format("had only {0} such {1}. These failed:{2}{3}",
                           countActual,
                           countActual.Pluralize("item"),
                           Environment.NewLine,
                           failuresExplained);
                       string escapedForFormatting = actual.Replace("{", "{{").Replace("}", "}}");
                       return string.Format(Evaluator.FormatExplanation(expected, escapedForFormatting));
                   };
        }
    }
}
